package com.hmall.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.search.domain.dto.ItemDTO;
import com.hmall.search.domain.dto.OrderDetailDTO;
import com.hmall.search.domain.po.Item;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.mapper.ItemMapper;
import com.hmall.search.service.ISearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
@Slf4j
public class ISearchServiceImpl extends ServiceImpl<ItemMapper, Item> implements ISearchService {

    private final ItemMapper itemMapper;
    private final RestHighLevelClient client= new RestHighLevelClient(
            RestClient.builder(HttpHost.create("http://192.168.121.130:9200")));
    @Override
    public void deductStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
        boolean r = false;
        try {
            r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
        } catch (Exception e) {
            throw new BizIllegalException("更新库存异常，可能是库存不足!", e);
        }
        if (!r) {
            throw new BizIllegalException("库存不足！");
        }
    }

    @Override
    public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
        return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
    }

    @Override
    public void restoreStock(List<OrderDetailDTO> items) {
        for (OrderDetailDTO item : items) {
            itemMapper.restoreStock(item);
        }
    }

    @Override
    public PageDTO<ItemDTO> listSearch(ItemPageQuery query) throws IOException {
        int pageNO= query.getPageNo(),pageSize= query.getPageSize();
        //2.创建request对象
        SearchRequest request = new SearchRequest("items");
        //3，设置request参数
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字
        if(StrUtil.isNotBlank(query.getKey()))
            boolQueryBuilder.must(QueryBuilders.matchQuery("name",query.getKey()));
        //品牌
        if(StrUtil.isNotBlank(query.getBrand()))
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand",query.getBrand()));
        //分类
        if(StrUtil.isNotBlank(query.getCategory()))
            boolQueryBuilder.filter(QueryBuilders.matchQuery("category",query.getCategory()));
        //最低价格
        if(query.getMinPrice()!=null)
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        //最高价格
        if(query.getMaxPrice()!=null)
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        //分页
        request.source().from((pageNO-1)*pageSize).size(pageSize);
        //排序字段
        if(StrUtil.isNotBlank(query.getSortBy()))
            request.source().sort(query.getSortBy(),query.getIsAsc()? SortOrder.ASC:SortOrder.DESC);
//        else
//            request.source().sort("updateTime",query.getIsAsc()?SortOrder.ASC:SortOrder.DESC);
        //修改算分规则
        request.source().query(QueryBuilders.functionScoreQuery(boolQueryBuilder,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD",true),
                                ScoreFunctionBuilders.weightFactorFunction(100)
                        )
                }).boostMode(CombineFunction.MULTIPLY));
        request.source().explain(true);
        //5.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //6.解析response
        SearchHit[] hits = response.getHits().getHits();
        long total = response.getHits().getTotalHits().value;
        List<ItemDoc> docList=new ArrayList<>();
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString();
            ItemDoc doc = JSONUtil.toBean(json, ItemDoc.class);
            docList.add(doc);
        }
        List<ItemDTO> itemDTOS = BeanUtil.copyToList(docList, ItemDTO.class);
        return new PageDTO<>(total,(long)pageSize,itemDTOS);
    }

    @Override
    public Map filters(ItemPageQuery query) throws IOException {
        SearchRequest request = new SearchRequest("items");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字
        if(StrUtil.isNotBlank(query.getKey()))
            boolQueryBuilder.must(QueryBuilders.matchQuery("name",query.getKey()));
        //品牌
        if(StrUtil.isNotBlank(query.getBrand()))
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand",query.getBrand()));
        //分类
        if(StrUtil.isNotBlank(query.getCategory()))
            boolQueryBuilder.filter(QueryBuilders.matchQuery("category",query.getCategory()));
        //最低价格
        if(query.getMinPrice()!=null)
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        //最高价格
        if(query.getMaxPrice()!=null)
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        String categoryAgg="category_agg";
        String brandAgg="brand_agg";
        request.source().query(boolQueryBuilder)
                .aggregation(AggregationBuilders.terms(categoryAgg).field("category"))
                .aggregation(AggregationBuilders.terms(brandAgg).field("brand"));
        SearchResponse response = client.search(request,RequestOptions.DEFAULT);
        HashMap<String, List<String>> map = new HashMap<>();
        Terms terms = response.getAggregations().get(categoryAgg);
        if(terms!=null){
            map.put("category",terms.getBuckets().stream()
                    .map(MultiBucketsAggregation.Bucket::getKeyAsString)
                    .collect(Collectors.toList()));
        }
        terms=response.getAggregations().get(brandAgg);
        if(terms!=null){
            map.put("brand",terms.getBuckets().stream()
                    .map(MultiBucketsAggregation.Bucket::getKeyAsString)
                    .collect(Collectors.toList()));
        }
        return map;
    }
}
